#pragma once

#include <iostream>
#include <sstream>

#include "TcpServer.hpp"
#include "Session.hpp"

const std::string HttpSep = "\r\n";
const std::string homepage = "index.html";
const std::string wwwroot = "./wwwroot";

class HttpRequest {
    public:
        HttpRequest() :_req_blank(HttpSep), _path(wwwroot) {}
        ~HttpRequest() {}

        bool getLine(std::string &str, std::string *line) {
            auto pos = str.find(HttpSep);
            if(pos == std::string::npos)
                return false;
            
            *line = str.substr(0, pos);
            //DLOG("getline: %s", line->c_str());
            str.erase(0, pos + HttpSep.size());
            return true;
        }

        void parse() {
		std::stringstream ss;
		ss >> _method >> _url >> _http_version;

		// Cookie: sessionid=1234
		std::string prefix = "Cookie: ";
		for(const auto &line : _req_header) {
			std::string cookie;
			if(strncmp(line.c_str(), prefix.c_str(), prefix.size() == 0)) {
				cookie = line.substr(prefix.size());
				_cookies.emplace_back(cookie);
				break;		
			}
		}		

		prefix = "session=";
		for(const auto &cookie : _cookies) {
			if(strncmp(cookie.c_str(), prefix.c_str(), prefix.size()) == 0) {
				_sessionid = cookie.substr(prefix.size());
			}
		}
       		
       	}

	std::string url() { return _url; }
	std::string sessionid() { return _sessionid; }
	
        bool deserialize(std::string &request) {
            std::string line;
            bool ok = getLine(request, &line);
            if(!ok) 
                return false;
            _req_line = line;

            while(true) {
                bool ok = getLine(request, &line);
                if(ok && line.empty()) {
                    _req_content = request;
                    break;
                }
                else if(ok && !line.empty()) {
                    _req_header.push_back(line);
                }
                else {
                    break;
                }
            }

            return true;
        }
        void debugHttp() {
            DLOG("_req_line: %s", _req_line.c_str());
            for(const auto &line : _req_header) {
                DLOG("---> %s", line.c_str());
            }
        }
    private:
        std::string _req_line;
        std::vector<std::string> _req_header;
        std::string _req_blank;
        std::string _req_content;

        std::string _method;
        std::string _url;
        std::string _http_version;
        std::string _path;
        std::string _suffix;

        std::vector<std::string> _cookies;
        std::string _sessionid;
};


const std::string BlankSep = " ";
const std::string LineSep = "\r\n";

class HttpResponse {
    public:
        HttpResponse() 
        :_http_version("HTTP/1.0"), _status_code(200), _status_code_desc("OK"), _resp_blank(LineSep)
        {}

        ~HttpResponse() {}

        void setCode(int code) { _status_code = code; }

        void setDesc(const std::string &desc) { _status_code_desc = desc; }

        void makeStatusLine() {
            _status_line = _http_version + BlankSep + std::to_string(_status_code) + BlankSep + _status_code_desc + LineSep;
        }

        void addHeader(const std::string &header) { _resp_header.push_back(header + LineSep); }

        void addContent(const std::string &content) { _resp_content = content; }

        std::string serialize() {
            makeStatusLine();
            std::string response_str = _status_line;
            for(const auto &header : _resp_header) 
                response_str += header;
            
            response_str += _resp_blank;
            response_str += _resp_content;
            return response_str;
        }

    private:
        std::string _status_line;
        std::vector<std::string> _resp_header;
        std::string _resp_blank;
        std::string _resp_content;

        std::string _http_version;
        int _status_code;
        std::string _status_code_desc;
};

class Http {
    public:
        Http(uint16_t port) {
            _tsvr = std::make_unique<TcpServer>(port, std::bind(&Http::HandlerHttp, this, std::placeholders::_1));
            _tsvr->Init();
        }
        ~Http() {}
        // 证明cookie可以被写入浏览器
        std::string ProveCookieWrite() {
            return "Set-Cookie: username=zhangsan;";
        }
        // 证明cookie 过期时间
        std::string ProveCookieTimeOut() {
            return "Set-Cookie: username=zhangsan, expires=" + expireTimeUseRfc1123(60) + ";";
        }
        // 证明cookie 资源路径
        std::string ProvePath() {
            return "Set-Cookie: username=zhangsan; path=/a/b;";
        }
        // 证明cookie 多个存在
        std::string ProveOtherCookie() {
            return "Set-Cookie: passward=123456; path=/a/b;";
        }
	// 证明session可以被写入
	std::string ProveSession(const std::string &session_id) {
		return "Set-Cookie: sessionid=" + session_id + ";";
	}

	// 处理方法
        std::string HandlerHttp(std::string request) {
      		HttpRequest req;
		req.deserialize(request);
		req.parse();
		req.debugHttp();
		
		HttpResponse resp;
		static int number = 0;
		if(req.url() == "/login") {
			std::string sessionid = req.sessionid();
			if(sessionid.empty()) {
				std::string user = "user-" + std::to_string(number++);
				Session::ptr s = std::make_shared<Session>(user, "logined");
				std::string id = _session_manager->addSession(s);
				DLOG("%s 被添加，sessionid是：%s", user.c_str(), sessionid.c_str());
				resp.addHeader(ProveSession(sessionid));
			}
		}
		else {
			std::string sessionid = req.sessionid();
			if(!sessionid.empty()) {
				Session::ptr s = _session_manager->getSession(sessionid);
				if(s != nullptr) {
					DLOG("%s 正在活跃!!!", s->_username.c_str());
				}
				else{
					DLOG("cookie: %s 已经过期，需要清理!!!", sessionid.c_str());	
				}
			}
		}

		resp.setCode(200);
		resp.setDesc("OK");
		resp.addHeader("Content-Type: text/html");
		resp.addContent("<html><h1>helloworld</h1></html>");
		return resp.serialize();
	}
        
        void run() {
            _tsvr->start();
        }
    private:
        // 设置过期时间相关函数
        std::string getMonthName(int month) {
            static std::vector<std::string> months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
            return months[month];
        }
        std::string getWeekDayName(int day) {
            static std::vector<std::string> weekdays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
            return weekdays[day];
        }
        // 使用Rfc1123格式表示
        std::string expireTimeUseRfc1123(int t) {
            time_t timeout = time(nullptr) + t;
            struct tm *tm = gmtime(&timeout);
            char timebuffer[1024];
            memset(timebuffer, 0, sizeof(timebuffer));

            snprintf(timebuffer, sizeof(timebuffer), "%s, %02d %s %d %02d:%02d:%02d UTF", 
                getWeekDayName(tm->tm_wday).c_str(), tm->tm_mday, getMonthName(tm->tm_mon).c_str(),
                tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
            return timebuffer;
        }
    private:
        std::unique_ptr<TcpServer> _tsvr;
	std::unique_ptr<SessionManager> _session_manager;
};
